import { $set } from '@feng3d/serialization';
import { BufferAttribute3D, BufferGeometryLoader3D, CanvasTexture3D, Color, Float32BufferAttribute3D, Mesh3D, MeshLambertMaterial3D, Node3D, OrthographicCamera3D, PerspectiveCamera3D, Scene3D, Sprite3D, SpriteMaterial3D, WebGLRenderer3D } from 'feng3d-three';
import GUI from 'lil-gui';
import '../../extension/controls/OrbitControls3D';
import { Lut3D } from '../../extension/math/Lut3D';

const rootPath = '../../';

let container: HTMLElement;

let perpCamera: PerspectiveCamera3D;
let orthoCamera: OrthographicCamera3D;
let renderer: WebGLRenderer3D;
let lut: Lut3D;

let mesh: Mesh3D;
let sprite: Sprite3D;
let scene: Scene3D;
let uiScene: Scene3D;

let params: { colorMap: string };

init();

function init()
{
    container = document.getElementById('container');

    scene = new Node3D().addComponent('Scene3D');
    scene.background = new Color(0xffffff);

    uiScene = new Node3D().addComponent('Scene3D');

    lut = new Lut3D();

    const width = window.innerWidth;
    const height = window.innerHeight;

    perpCamera = new Node3D().addComponent('PerspectiveCamera3D', { fov: 60, aspect: width / height, near: 1, far: 100 });
    perpCamera.entity.position.set(0, 0, 10);
    scene.entity.addChild(perpCamera.entity);

    orthoCamera = new Node3D().addComponent('OrthographicCamera3D', { left: -1, right: 1, top: 1, bottom: -1, near: 1, far: 2 });
    orthoCamera.entity.position.set(0.5, 0, 1);

    sprite = new Node3D().addComponent('Sprite3D', {
        material: new SpriteMaterial3D({
            map: new CanvasTexture3D(lut.createCanvas())
        })
    });
    sprite.entity.scale.x = 0.125;
    uiScene.entity.addChild(sprite.entity);

    mesh = new Node3D().addComponent('Mesh3D', {
        material: $set(new MeshLambertMaterial3D(), {
            side: 'DoubleSide',
            color: { value: 0xF5F5F5 },
            vertexColors: true
        })
    });
    scene.entity.addChild(mesh.entity);

    params = {
        colorMap: 'rainbow',
    };
    loadModel();

    const pointLight = new Node3D().addComponent('PointLight3D', { color: { value: 0xffffff }, intensity: 1 });
    perpCamera.entity.addChild(pointLight.entity);

    renderer = scene.addComponent('WebGLRenderer3D', { parameters: { antialias: true } });
    renderer.autoClear = false;
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(width, height);
    container.appendChild(renderer.domElement);

    window.addEventListener('resize', onWindowResize);

    const controls = scene.addComponent('OrbitControls3D', { camera: perpCamera, domElement: renderer.domElement });
    controls.emitter.on('change', render);

    const gui = new GUI();

    gui.add(params, 'colorMap', ['rainbow', 'cooltowarm', 'blackbody', 'grayscale']).onChange(function ()
    {
        updateColors();
        render();
    });
}

function onWindowResize()
{
    const width = window.innerWidth;
    const height = window.innerHeight;

    perpCamera.aspect = width / height;
    perpCamera.updateProjectionMatrix();

    renderer.setSize(width, height);
    render();
}

function render()
{
    renderer.clear();
    renderer.render(scene, perpCamera);
    renderer.render(uiScene, orthoCamera);
}

function loadModel()
{
    const loader = new BufferGeometryLoader3D();
    loader.load(`${rootPath}models/json/pressure.json`, function (geometry)
    {
        geometry.center();
        geometry.computeVertexNormals();

        // default color attribute
        const colors = [];

        for (let i = 0, n = geometry.attributes.position.count; i < n; ++i)
        {
            colors.push(1, 1, 1);
        }

        geometry.setAttribute('color', new Float32BufferAttribute3D(colors, 3));

        mesh.geometry = geometry;
        updateColors();

        render();
    });
}

function updateColors()
{
    lut.setColorMap(params.colorMap);

    lut.setMax(2000);
    lut.setMin(0);

    const geometry = mesh.geometry;
    const pressures = geometry.attributes.pressure as BufferAttribute3D;
    const colors = geometry.attributes.color as BufferAttribute3D;

    for (let i = 0; i < pressures.array.length; i++)
    {
        const colorValue = pressures.array[i];

        const color = lut.getColor(colorValue);

        if (color === undefined)
        {
            console.log('Unable to determine color for value:', colorValue);
        }
        else
        {
            colors.setXYZ(i, color.r, color.g, color.b);
        }
    }

    colors.needsUpdate = true;

    const map = sprite.material.map;
    lut.updateCanvas(map.image);
    map.needsUpdate = true;
}
